package com.wmh.baseservice.common.websocket.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wmh.baseservice.common.ex.ServiceException;
import com.wmh.baseservice.common.redis.config.RedisConfig;
import com.wmh.baseservice.common.redis.utils.RedisUtil;
import com.wmh.baseservice.common.shiro.config.ShiroConfig;
import com.wmh.baseservice.common.shiro.jwt.JwtTokenUtil;
import com.wmh.baseservice.common.utils.CommUtil;
import com.wmh.baseservice.common.utils.encrypt.Base64Util;
import com.wmh.baseservice.common.utils.returnbody.ResultCodeInfo;
import com.wmh.baseservice.common.wechat.mp.template.WxSubMsgUtils;
import com.wmh.baseservice.merchant.pojo.MerchantInfo;
import com.wmh.baseservice.merchant.service.MerchantInfoService;
import com.wmh.baseservice.system.pojo.SysChat;
import com.wmh.baseservice.system.service.SysChatService;
import com.wmh.baseservice.user.pojo.UsInfo;
import com.wmh.baseservice.user.service.UsInfoService;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * websocket聊天接口
 */
@Component
@ServerEndpoint(value = "/websocket/{token}/{inquiryId}")
public class ChatController {

    //静态变量，用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int usOnlineCount = 0;
    private static int merchantOnlineCount = 0;
    //impl注入
    private static UsInfoService usInfoService;
    private static MerchantInfoService merchantInfoService;
    private static SysChatService sysChatService;
    private static RedisUtil redisUtil;

    private static final ConcurrentHashMap<Long, Session> userMap = new ConcurrentHashMap<>();
    private static final ConcurrentHashMap<Long, Session> merchantMap = new ConcurrentHashMap<>();


    @Autowired
    public void setService(MerchantInfoService merchantInfoService, UsInfoService usInfoService, RedisUtil redisUtil, SysChatService sysChatService) {
        ChatController.merchantInfoService = merchantInfoService;
        ChatController.usInfoService = usInfoService;
        ChatController.redisUtil = redisUtil;
        ChatController.sysChatService = sysChatService;
    }

    /**
     * 加入连接
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        JSONObject result = verifyJwt(token);
        String resultObj = result.getString("result");
        Long id = result.getLong("id");
        String type = result.getString("type");
        if (!"success".equals(resultObj)) {
            throw new ServiceException(ResultCodeInfo.TOKEN_EXPIRE.getResult());
        }
        //根据类型存入不同集合中
        if ("user".equals(type)) {
            if (!userMap.containsKey(id)) {
                userMap.put(id, session);
                addOnlineCount(1);
                System.out.printf("有用户连接! id为%s,当前在线人数为%s", id, getOnlineCount(1));
            }
        } else {
            if (!merchantMap.containsKey(id)) {
                merchantMap.put(id, session);
                addOnlineCount(2);
                System.out.printf("有商家连接! id为%s,当前在线人数为%s", id, getOnlineCount(2));
            }
        }
    }

    /**
     * 关闭连接
     */
    @OnClose
    public void onclose(@PathParam("token") String token, Session session) {
        JSONObject result = verifyJwt(token);
        String resultObj = result.getString("result");
        Long id = result.getLong("id");
        String type = result.getString("type");
        if (!"success".equals(resultObj)) {
            throw new ServiceException(ResultCodeInfo.TOKEN_EXPIRE.getResult());
        }
        if ("user".equals(type)) {
            userMap.remove(id);
            subOnlineCount(1);
            System.out.printf("有用户连接关闭! 用户名为%s,当前在线人数为%s", id, getOnlineCount(1));
        } else {
            merchantMap.remove(id);
            subOnlineCount(2);
            System.out.printf("有商家关闭! 用户名为%s,当前在线人数为%s", id, getOnlineCount(2));
        }

    }

    /**
     * 发消息
     */
    @OnMessage
    public void onmessage(String message, @PathParam("token") String token, @PathParam("inquiryId") Long inquiryId) throws IOException {
        JSONObject result = verifyJwt(token);
        String resultObj = result.getString("result");
        Long id = result.getLong("id");
        String type = result.getString("type");
        String mpOpenId="";
        if (!"success".equals(resultObj)) {
            throw new ServiceException(ResultCodeInfo.TOKEN_EXPIRE.getResult());
        }
        Session session;
        SysChat sysChat = new SysChat();
        JSONObject object = JSONObject.parseObject(message);
        message = object.getString("text");
        String msgType = object.getString("type");
        switch (msgType) {
            case "image":
                 message= Base64Util.generateImage("chat/", message);
                 sysChat.setW(object.getInteger("w"));
                 sysChat.setH(object.getInteger("h"));
                 break;
            case "text":
                break;
            case "voice":
                sysChat.setLength(object.getString("length"));
                break;
            case "redEnvelope":
                sysChat.setHasReceived(0).setRid(object.getLong("rid"));
                break;
        }

        //添加聊天记录
        sysChat.setContent(message).setMsgType(msgType).setUid(id).setStatus(0);
        //redisKey格式:项目名CHAT-用户id:商家id
        String key = RedisConfig.KEY_INFO + "CHAT-";
        //如果当前发消息的身份是用户，那么从商家中拿id,反之则从用户中拿
        if ("user".equals(type)) {
            session = merchantMap.get(inquiryId);
            sysChat.setType("user").setSendId(inquiryId).setSendType(1).setCreateTime(new Date());
            String suffix = id + ":" + inquiryId;
            sysChat.setSessionId(suffix);
            key = key + suffix;
            //获取商家公众号openId
            UsInfo usInfo = usInfoService.getOne(new QueryWrapper<UsInfo>().eq(UsInfo.MERCHANT_ID, inquiryId));
            if (CommUtil.checkNull(usInfo,usInfo.getMpOpenId())){
                mpOpenId=usInfo.getMpOpenId();
            }
        } else {
            session = userMap.get(inquiryId);
            sysChat.setType("merchant").setSendId(inquiryId).setSendType(0).setCreateTime(new Date());
            String suffix = inquiryId + ":" + id;
            sysChat.setSessionId(suffix);
            key = key + suffix;
            //获取用户公众号openId
            UsInfo usInfo = usInfoService.getById(id);
            if (CommUtil.checkNull(usInfo,usInfo.getMpOpenId())){
                mpOpenId=usInfo.getMpOpenId();
            }
        }
        if (CommUtil.checkNull(mpOpenId)){
            WxSubMsgUtils.newMessage(mpOpenId);
        }

        //先查询redis中是否有商家+用户的对话id键值对存在，如果有，那么直接获取该键值对，往里面添加一条数据，如果没有，那么新建键值对
        if (!redisUtil.hasKey(key)) {
            //添加新聊天集合
            List<SysChat> sysChats = new ArrayList<>();
            sysChats.add(sysChat);
            redisUtil.set(key, sysChats);
        } else {
            //追加聊天记录
            List<SysChat> sysChats = (List<SysChat>) redisUtil.get(key);
            sysChats.add(sysChat);
            redisUtil.set(key, sysChats);
        }
        //数据库插入
        if (!sysChatService.save(sysChat)) {
            throw new ServiceException("插入聊天记录失败");
        }
        String json = JSONObject.toJSONString(sysChat);
        if (CommUtil.checkNull(session)) {
            sendMessage(json, session, inquiryId, type);
        }

    }


    @OnError
    public void onError(Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }


    /**
     * 解析token
     */
    public JSONObject verifyJwt(String jwt) {
        JSONObject object = new JSONObject(true);
        object.put("result", "success");
        //验证user是否存在
        jwt = jwt.replace("Bearer_", "");
        if (!JwtTokenUtil.isTokenExpired(jwt)) {
            object.put("result", "error");
        }
        String users = JwtTokenUtil.getUserInfoFromToken(jwt).get("account").toString();
        String[] usersSplit = users.split(":");
        if (usersSplit.length != 2) {
            throw new UnauthenticatedException();
        }
        String username = usersSplit[0];
        if (username.contains(ShiroConfig.SHIRO_USER) || username.contains(ShiroConfig.SHIRO_SSO)) {
            UsInfo usInfo;
            if (username.contains(ShiroConfig.SHIRO_USER)) {
                //账号(手机号)密码
                usInfo = usInfoService.getOneForUpdate(new QueryWrapper<UsInfo>().eq(UsInfo.PHONE, username.replace(ShiroConfig.SHIRO_USER, "")));
            } else {
                usInfo = usInfoService.getOneForUpdate(new QueryWrapper<UsInfo>().eq(UsInfo.OPEN_ID, username.replace(ShiroConfig.SHIRO_SSO, "")));
            }
            if (!CommUtil.checkNull(usInfo)) {
                object.put("result", "error");
            }
            object.put("type", "user");
            object.put("id", usInfo.getId());
        } else if (username.contains(ShiroConfig.SHIRO_MERCHANT)) {
            MerchantInfo merchantInfo;
            String replace = username.replace(ShiroConfig.SHIRO_MERCHANT, "");
            QueryWrapper<MerchantInfo> eq = new QueryWrapper<MerchantInfo>().eq(MerchantInfo.ACCOUNT, replace);
            merchantInfo = merchantInfoService.getOneForUpdate(eq);
            if (!CommUtil.checkNull(merchantInfo)) {
                object.put("result", "error");
            }
            object.put("type", "merchant");
            object.put("id", merchantInfo.getId());
        } else {
            object.put("result", "error");
        }
        return object;
    }


    /**
     * 服务端推送消息
     */
    public void sendMessage(String msg, Session session, Long inquiryId, String type) throws IOException {
        if (session.isOpen()) {
            session.getBasicRemote().sendText(msg);
        }
    }


    public static synchronized int getOnlineCount(Integer type) {
        if (type == 1) {
            return usOnlineCount;
        } else {
            return merchantOnlineCount;
        }
    }

    public static synchronized void addOnlineCount(Integer type) {
        if (type == 1) {
            usOnlineCount++;
        } else {
            merchantOnlineCount++;
        }
    }

    public static synchronized void subOnlineCount(Integer type) {
        if (type == 1) {
            usOnlineCount--;
        } else {
            merchantOnlineCount--;
        }
    }
}
